import React, {useEffect} from "react";
import gsap from "gsap";
import "./CustomCursor.css";

const lerp = (a, b, n) => (1 - n) * a + n * b;
const map = (x, a, b, c, d) => ((x - a) * (d - c)) / (b - a) + c;

const CustomCursor = () => {
    useEffect(() => {
        const cursor = {x: 0, y: 0};
        const cursorElements = Array.from(document.querySelectorAll(".cursor"));
        const feTurbulence = document.querySelector("#cursor-filter > feTurbulence");
        const bounds = cursorElements[0].getBoundingClientRect();
        let radius = 20;
        const radiusOnEnter = 50;

        const renderedStyles = {
            tx: {previous: 0, current: 0, amt: 0.15},
            ty: {previous: 0, current: 0, amt: 0.15},
            radius: {previous: 20, current: 20, amt: 0.15},
            opacity: {previous: 1, current: 1, amt: 0.15},
        };

        const primitiveValues = {turbulence: 0};

        const createFilterTimeline = () => {
            const turbulenceValues = {from: 0.01, to: 0.04};
            return gsap.timeline({
                paused: true,
                onStart: () => {
                    feTurbulence.setAttribute("seed", Math.round(gsap.utils.random(1, 20)));
                    cursorElements[0].querySelector(".cursor__inner").style.filter = `url(#cursor-filter)`;
                    renderedStyles.opacity.current = 1;
                },
                onUpdate: () => {
                    feTurbulence.setAttribute("baseFrequency", primitiveValues.turbulence);
                    renderedStyles.opacity.current = renderedStyles.opacity.previous = map(
                        primitiveValues.turbulence,
                        turbulenceValues.from,
                        turbulenceValues.to,
                        1,
                        0
                    );
                    renderedStyles.radius.current = renderedStyles.radius.previous = map(
                        primitiveValues.turbulence,
                        turbulenceValues.from,
                        turbulenceValues.to,
                        +radius,
                        radiusOnEnter
                    );
                },
                onComplete: () => {
                    cursorElements[0].querySelector(".cursor__inner").style.filter = "none";
                    renderedStyles.radius.current = renderedStyles.radius.previous = radius;
                },
            }).to(primitiveValues, {
                duration: 1.5,
                ease: "power1",
                startAt: {turbulence: turbulenceValues.from},
                turbulence: turbulenceValues.to,
            });
        };

        const filterTimeline = createFilterTimeline();

        const render = () => {
            renderedStyles.tx.current = cursor.x - bounds.width / 2;
            renderedStyles.ty.current = cursor.y - bounds.height / 2;
            for (const key in renderedStyles) {
                renderedStyles[key].previous = lerp(
                    renderedStyles[key].previous,
                    renderedStyles[key].current,
                    renderedStyles[key].amt
                );
            }
            cursorElements.forEach((el) => {
                el.style.transform = `translateX(${renderedStyles.tx.previous}px) translateY(${renderedStyles.ty.previous}px)`;
                el.querySelector(".cursor__inner").setAttribute("r", renderedStyles.radius.previous);
                el.style.opacity = renderedStyles.opacity.previous;
            });
            requestAnimationFrame(render);
        };

        const onMouseMove = (ev) => {
            cursor.x = ev.clientX;
            cursor.y = ev.clientY;
        };

        const onMouseEnter = () => {
            renderedStyles.opacity.current = 1;
            filterTimeline.restart();
        };

        const onMouseLeave = () => {
            cursorElements.forEach((el) => {
                el.querySelector(".cursor__inner").style.filter = "none";
            });
            filterTimeline.kill();
            renderedStyles.radius.current = radius;
            renderedStyles.opacity.current = 1;
        };

        window.addEventListener("mousemove", onMouseMove);
        document.querySelectorAll("a").forEach((link) => {
            link.addEventListener("mouseenter", onMouseEnter);
            link.addEventListener("mouseleave", onMouseLeave);
        });

        requestAnimationFrame(render);

        return () => {
            window.removeEventListener("mousemove", onMouseMove);
        };
    }, []);

    return (
        <>
            <svg className="cursor" width="140" height="140" viewBox="0 0 140 140">
                <defs>
                    <filter
                        id="cursor-filter"
                        x="-50%"
                        y="-50%"
                        width="200%"
                        height="200%"
                        filterUnits="objectBoundingBox"
                    >
                        <feTurbulence
                            type="fractalNoise"
                            seed="3"
                            baseFrequency="0"
                            numOctaves="1"
                            result="warp"
                        />
                        <feDisplacementMap
                            xChannelSelector="R"
                            yChannelSelector="G"
                            scale="15"
                            in="SourceGraphic"
                        />
                    </filter>
                </defs>
                <circle className="cursor__inner" cx="70" cy="70" r="20"/>
            </svg>
            <svg className="cursor" width="140" height="140" viewBox="0 0 140 140" data-amt="0.13">
                <circle className="cursor__inner" cx="70" cy="70" r="24"/>
            </svg>
        </>
    );
};

export default CustomCursor;



